#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <map>
#include <utility>
#include <algorithm>
#include <set>
#include <exception>
#include <sstream>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
#include <fstream>
#include <stack>
#include <boost/algorithm/string.hpp>

#include <boost/xpressive/xpressive.hpp>
#include <boost/regex.hpp>

using namespace std;
using namespace boost::algorithm;

#include "kaldi_scp_process.h"
#include "string_manager.h"




StdThread::StdThread()
{
}

StdThread::~StdThread()
{
}

bool StdThread::Enter(){
	stream_mtx.try_lock();
	return true;
}

bool StdThread::Leave(){
	stream_mtx.unlock();
	return true;
}



KaldiScpProcess::KaldiScpProcess(const char* kaldi_scp_file)
{
	if (CheckFile(kaldi_scp_file)){
		return;
	}
	mKaldi_scp_file = kaldi_scp_file;
}

KaldiScpProcess::~KaldiScpProcess()
{
}

std::ifstream& KaldiScpProcess::OpenKaldiScpFile(const char* kaldi_scp_file /* = nullptr */){
	if (! kaldi_scp_file){
		kaldi_scp_file = this->mKaldi_scp_file.c_str();
		if (! kaldi_scp_file){
			return mIs;
		}
	}
	mIs.open(kaldi_scp_file, ios::in);
	if (mIs.is_open() && mIs.good()){
		return mIs;
	}
	else
	{
		std::cout << "can not open kaldi scp file\n";
		exit(-1);
	}
}

const std::set<string>& KaldiScpProcess::get_all_keys(){
	if (mAllkeys.size() == 0){
		//start to compute the keys
		try
		{
			mIs.open(mKaldi_scp_file.c_str(), ios::in);
			StringManager key_value_line_manager;
			if (mIs.good()){
				string line, this_key, this_value;
				std::vector<string> line_units;
				while (!mIs.eof())
				{
					std::getline(mIs, line, '\n');
					key_value_line_manager.set_string(line);
					key_value_line_manager.SplitStringtoSpecializedSize(line.c_str(), " \t", &line_units, 1);
					if (line_units.size() == 2){
						this_key = line_units[0];
						this_value = line_units[1];
						mAllkeys.insert(this_key);
						mAllkeyAarray.push_back(this_key);
					}

				}
			}
			mIs.close();
			return mAllkeys;
		}
		catch (exception* e)
		{
			std::cout << e->what() << std::endl;
			exit(-1);
		}
	}else{
		return mAllkeys;
	}
}


string FixPdf(const char* pdf_entry){
	string standard_pdf = pdf_entry;
	if (standard_pdf.empty()){
		return standard_pdf;
	}
	//see it is key value mode or pure value mode
	boost::trim(standard_pdf);
	string fix_result = "";
	if (standard_pdf.compare("[")){
		fix_result = FixPdfContent(standard_pdf.c_str());
		return fix_result;
	} else{
		StringManager sm;
		sm.set_string(standard_pdf);
		std::vector<string> key_and_value;
		sm.SplitStringtoSpecializedSize(standard_pdf.c_str(),
			" \t", &key_and_value, 1);
		boost::trim(key_and_value[1]);
		if (key_and_value[1].compare("[") == 0){
			string value_result = FixPdf(key_and_value[1].c_str());
			fix_result = key_and_value[0] + " " + value_result;
			return fix_result;
		}
		else{
			std::cout << "Pdf format error !" << std::endl;
			exit(-1);
		}
	}
}


string FixPdfContent(const char* pdf_content){
	string fix_result = pdf_content, fixing_string = pdf_content;
	if (fix_result.empty()){
		return fix_result;
	}
	//here we use stack to check and fix the standard pdf format
	// the standard format is [ number1 number2 ] [ number3 number4 ] [ ...
	std::vector<string> line_units;
	boost::split(line_units, fixing_string, boost::algorithm::is_any_of(" \t"), boost::algorithm::token_compress_off);
	//we should fix the original format to standard format
	int i = 0, group_size = 4, group_index=0;
	vector<string> fixed_array;
	fixed_array.resize(line_units.size());
	std::vector<string> group;
	boost::regex number_regex("\\d+");
	for (i = 0; i < line_units.size();i++)
	{
		string curr_symbol = line_units[i];
		if (group.size() == 0 && curr_symbol.compare("[") == 0){
			group.push_back(curr_symbol);
			continue;
		}
		if (group.size() == 1 && boost::regex_match(curr_symbol, number_regex)){
			group.push_back(curr_symbol);
			continue;
		}
		if (group.size() == 2 && boost::regex_match(curr_symbol, number_regex)){
			group.push_back(curr_symbol);
			continue;
		}
		if (group.size() == 3 && curr_symbol.compare("]") == 0){
			group.push_back(curr_symbol);
			assert(group.size() == group_size);
			std::copy(group.begin(), group.end(), fixed_array.begin() + group_index * group_size);
			group_index++;
			group.clear();
		}

	}
	int pdf_real_size = group_size * group_index;
	fixed_array.resize(pdf_real_size);
	fix_result = boost::join(fixed_array, " ");
	return fix_result;
}
